﻿ // Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Components;

/// <summary>
/// <see cref="NavbarDropdown"/> 组件
/// 导航栏组件的一部分，用于显示其中的下拉菜单，其中可以包括导航项和分隔符
/// </summary>
public sealed partial class NavbarDropdown : IDisposable
{
    /// <summary>
    /// 获得 样式集合
    /// </summary>
    /// <returns></returns>
    public string? ClassString => CssBuilder.Default()
        .AddClass("dropdown navbar-h-dropdown", _dropdownState.IsHorizontal)
        .AddClass("navbar-v-dropdown", !_dropdownState.IsHorizontal)
        .AddClass("show", _dropdownState.DropdownVisible)
        .AddClassFromAttributes(AdditionalAttributes)
        .Build();

    private NavbarItemState _itemState = new();

    /// <summary>
    /// 父级下拉菜单的状态
    /// </summary>
    private NavbarDropdownState _parentDropdownState = new();

    /// <summary>
    /// 获得 当前 <see cref="NavbarDropdown"/> 组件状态。
    /// </summary>
    private NavbarDropdownState _dropdownState = new();

    /// <summary>
    /// 放置此 <see cref="NavbarDropdown"/> 的 <see cref="NavbarItem"/> 组件。
    /// </summary>
    [CascadingParameter]
    public NavbarItem? ParentItem { get; set; }

    /// <summary>
    /// 获得/设置 父级 <see cref="NavbarItem"/> 组件的状态
    /// </summary>
    [CascadingParameter]
    public NavbarItemState ParentItemState
    {
        get => _itemState;
        set
        {
            if (_itemState != value)
            {
                _itemState = value;
                _dropdownState = _dropdownState with
                {
                    Visible = value.Visible,
                    IsRTL = value.IsRTL,
                    Mode = value.Mode,
                    Theme = value.Theme,
                    Id = value.Id,
                    IsTail = value.IsTail,
                };
                if (!_dropdownState.Visible)
                {
                    Visible = false;
                }
            }
        }
    }

    /// <summary>
    /// 获得/设置 父级 <see cref="NavbarDropdown"/> 组件的状态
    /// </summary>
    [CascadingParameter]
    public NavbarDropdownState ParentDropdownState
    {
        get => _parentDropdownState;
        set
        {
            if (_parentDropdownState != value)
            {
                _parentDropdownState = value;
                _dropdownState = _dropdownState with { Indent = value.Indent + 1 };
            }
        }
    }

    /// <summary>
    /// 获得/设置 父级 <see cref="NavbarDropdown"/> 组件
    /// </summary>
    [CascadingParameter]
    public NavbarDropdown? ParentDropdown { get; set; }

    /// <summary>
    /// 获得/设置 子组件内容
    /// </summary>
    [Parameter]
    public string? Key { get; set; }

    /// <summary>
    /// 获得/设置 控制下拉菜单及其所有子控件是否可见。
    /// </summary>
    [Parameter]
    public bool Visible
    {
        get => _dropdownState.DropdownVisible;
        set
        {
            if (value != _dropdownState.DropdownVisible)
            {
                _dropdownState = _dropdownState with { DropdownVisible = value };
                VisibleChanged.InvokeAsync(value);
            }
        }
    }

    /// <summary>
    /// 获得/设置 可见性变化事件
    /// </summary>
    [Parameter]
    public EventCallback<bool> VisibleChanged { get; set; }

    /// <summary>
    /// 获得/设置 子组件内容
    /// </summary>
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    /// <summary>
    /// 当前下拉菜单的下拉菜单子项。
    /// </summary>
    internal List<NavbarDropdown> ChildDropdowns { get; private set; } = new();

    /// <summary>
    /// 延迟到组件渲染完成后执行的方法队列
    /// </summary>
    internal Queue<Func<ValueTask>> ExecuteAfterRenderQueue { get; set; } = new();

    /// <summary>
    /// 跟踪下拉列表是否处于应该关闭的状态。
    /// </summary>
    internal bool ShouldClose { get; set; } = false;

    /// <summary>
    /// 跟踪下拉列表是否刚刚切换，忽略可能会关闭下拉列表的下拉项单击。
    /// </summary>
    internal bool WasJustToggled { get; private set; } = false;

    /// <summary>
    /// 获得 指示当前下拉菜单是否被包含在另外一个下拉菜单中
    /// </summary>
    internal bool IsDropdownSubmenu => ParentDropdown is not null;

    /// <summary>
    /// 获得 指示当前下拉菜单是否包含有下拉菜单
    /// </summary>
    internal bool HasSubmenu => ChildDropdowns.Any();

    /// <summary>
    /// 获得 指示光标是否悬浮在当前下拉菜单上
    /// </summary>
    internal bool HasHovered => _dropdownState.HoverMenu || _dropdownState.HoverToggler;

    /// <summary>
    /// 获得 下拉菜单及其所有子控件可见状态。
    /// </summary>
    private string VisibleString => _dropdownState.DropdownVisible ? "true" : "false";

    /// <summary>
    /// SetParametersAsync
    /// </summary>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public override Task SetParametersAsync(ParameterView parameters)
    {
        // 这是双向绑定正常工作所必需的。
        // 否则，内部值的设置顺序将不正确。
        if (parameters.TryGetValue<bool>(nameof(Visible), out var visible))
        {
            _dropdownState = _dropdownState with { DropdownVisible = visible };
        }

        return base.SetParametersAsync(parameters);
    }

    /// <summary>
    /// OnInitializedAsync
    /// </summary>
    /// <returns></returns>
    protected override Task OnInitializedAsync()
    {
        ParentItem?.NotifyDropdownInitialized();
        ParentDropdown?.NotifyChildDropdownInitialized(this);

        return base.OnInitializedAsync();
    }

    /// <summary>
    /// OnAfterRender
    /// </summary>
    /// <param name="firstRender"></param>
    protected override void OnAfterRender(bool firstRender)
    {
        WasJustToggled = false;

        base.OnAfterRender(firstRender);
    }

    /// <summary>
    /// 显示下拉菜单。
    /// </summary>
    /// <returns></returns>
    public async Task Show()
    {
        if (!Visible)
        {
            Visible = true;
            await InvokeAsync(StateHasChanged);
        }
    }

    /// <summary>
    /// 隐藏下拉菜单。
    /// </summary>
    /// <param name="hideAll">指示是否需要隐藏当前下拉菜单及其所有父下拉菜单。</param>
    /// <returns></returns>
    public async Task Hide(bool hideAll = false)
    {
        if (!Visible)
        {
            return;
        }

        if (ParentDropdown is not null && (ParentDropdown.ShouldClose || hideAll))
        {
            await ParentDropdown.Hide(hideAll);
        }

        Visible = false;

        await InvokeAsync(StateHasChanged);
    }

    /// <summary>
    /// 切换下拉菜单的可见性。
    /// </summary>
    /// <returns></returns>
    public Task Toggle()
    {
        // 当菜单处于垂直“弹出”样式模式时，不允许切换。
        // 这将由下面的鼠标操作来处理。
        if (!ParentItemState.IsHorizontal && !_dropdownState.IsMixinDisplay && !_dropdownState.IsPopoutDisplay)
        {
            return Task.CompletedTask;
        }

        SetWasJustToggled(true);

        Visible = !Visible;

        //更新上级菜单滚动指示器状态
        ExecuteAfterRenderQueue.Enqueue(UpdateParentMenuIndicator);

        return InvokeAsync(StateHasChanged);
    }

    /// <summary>
    /// 在当前下拉列表和每个现有父下拉列表上设置Toggled标志。
    /// </summary>
    /// <param name="wasToggled"></param>
    internal void SetWasJustToggled(bool wasToggled)
    {
        WasJustToggled = wasToggled;
        ParentDropdown?.SetWasJustToggled(wasToggled);
    }

    /// <summary>
    /// 鼠标移入处理事件
    /// </summary>
    /// <returns></returns>
    public async Task OnMouseEnterHandler()
    {
        ShouldClose = false;

        if (ParentItemState.IsHorizontal || _dropdownState.IsMixinDisplay || _dropdownState.IsPopoutDisplay)
        {
            return;
        }

        await Show();
    }

    /// <summary>
    /// 鼠标移出处理事件
    /// </summary>
    /// <returns></returns>
    public async Task OnMouseLeaveHandler()
    {
        ShouldClose = true;

        if (ParentItemState.IsHorizontal || _dropdownState.IsMixinDisplay || _dropdownState.IsPopoutDisplay)
        {
            return;
        }

        await Hide();
    }

    /// <summary>
    /// 更新子下拉菜单组件引用
    /// </summary>
    /// <param name="dropdown"></param>
    internal void NotifyChildDropdownInitialized(NavbarDropdown dropdown) => ChildDropdowns.Add(dropdown);

    /// <summary>
    /// 移除子下拉菜单组件
    /// </summary>
    internal void NotifyChildDropdownRemoved(NavbarDropdown dropdown) => ChildDropdowns.Remove(dropdown);

    /// <summary>
    /// 移除子下拉菜单组件
    /// </summary>
    internal ValueTask UpdateParentMenuIndicator() => JSRuntime.InvokeVoidAsync($"{_dropdownState.Id}_menu", "bb_update_navbar_menu_indicator");

    /// <summary>
    /// Dispose
    /// </summary>
    /// <param name="disposing"></param>
    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            ParentDropdown?.NotifyChildDropdownRemoved(this);
        }
    }

    /// <summary>
    /// Dispose
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}
